package com.nulldev.util.graphics.renderIt.renderer.swing.nitrous2.renderers;

import java.awt.*;
import java.awt.peer.ComponentPeer;
import java.lang.reflect.Method;

import com.nulldev.util.JVM.reflections.ClassCache;
import com.nulldev.util.graphics.renderIt.renderer.swing.nitrous2.Renderer;

/**
 * Represents a renderer using the OpenGL Extension to the X Window System (GLX)
 * API.
 *
 * @see <a href="https://en.wikipedia.org/wiki/GLX">GLX</a>
 */
public class GLXRenderer extends Renderer {
	/** Class name of the surface's peer component. */
	private final static String PEER_CLASS_NAME = "sun.awt.X11ComponentPeer";
	/** Class name of the surface to render on. */
	private final static String SURFACE_CLASS_NAME = "sun.java2d.x11.X11SurfaceData";

	/**
	 * Constructs a new GLXRenderer.
	 *
	 * @param peer Component that the surface is displayed on. (e.g. An instance of
	 *             java.awt.Panel)
	 *
	 * @throws ClassNotFoundException If the peer or surface classes cannot be
	 *                                found. This will occur if the JRE/JDK of this
	 *                                machine doesn't support this renderer.
	 */
	public GLXRenderer(final ComponentPeer peer) throws ClassNotFoundException {
		super(peer, PEER_CLASS_NAME, SURFACE_CLASS_NAME);
	}

	@Override
	public GraphicsConfiguration getGraphicsConfig() {
		try {
			final GraphicsConfiguration currentGraphicsConfiguration = super.getGraphicsConfig();

			final Class<?> glxGraphicsConfig = ClassCache.forName("sun.java2d.opengl.GLXGraphicsConfig");
			final Class<?> x11GraphicsDevice = ClassCache.forName("sun.awt.X11GraphicsDevice");
			final Class<?> x11GraphicsConfig = ClassCache.forName("sun.awt.X11GraphicsConfig");

			final Method getVisualMethod = x11GraphicsConfig.getMethod("getVisual");
			final int visual = (int) getVisualMethod.invoke(currentGraphicsConfiguration);

			final Method getConfigMethod = glxGraphicsConfig.getMethod("getConfig", x11GraphicsDevice, int.class);

			final Object glxGraphicsCfg = getConfigMethod.invoke(null, currentGraphicsConfiguration.getDevice(), visual);

			if (this.ogl_string == null) {
				this.ogl_string = "N/A";
			}

			return (GraphicsConfiguration) glxGraphicsCfg;
		} catch (final Exception e) {
			// todo Tons of exceptions can be thrown in the above code, catch it or
			// something.
			// Otherwise, we fall back to the default and hope for the best.
			e.printStackTrace();
			return super.getGraphicsConfig();
		}
	}

	private String ogl_string;

	/**
	 * Retrieves this renderer's name.
	 *
	 * @return This renderer's name.
	 */
	@Override
	public String getName() {
		return "OpenGL (GLX)";
	}

	@Override
	public String getRendererString() {
		return "OpenGL (GLX; " + (this.ogl_string != null ? this.ogl_string : "N/A") + ")";
	}

	/**
	 * Determines whether the GLXRenderer is supported on this machine.
	 *
	 * @return Whether the GLXRenderer is supported on this machine.
	 */
	public static boolean isSupported() {
		boolean isSupported = Renderer.isSupported(PEER_CLASS_NAME, SURFACE_CLASS_NAME);

		try {
			final Class<?> x11GraphicsEnvironment = ClassCache.forName("sun.awt.X11GraphicsEnvironment");
			final Method isGLXAvailableMethod = x11GraphicsEnvironment.getMethod("isGLXAvailable");
			isSupported &= (boolean) isGLXAvailableMethod.invoke(null);
		} catch (final Exception e) {
			isSupported = false;
		}

		return isSupported;
	}
}